home *** CD-ROM | disk | FTP | other *** search
- Frequently Asked Questions (FAQS);faqs.352
-
-
-
- - PROGV. PROGV binds dynamic variables and is often misused in
- conjunction with EVAL, which uses the dynamic environment.
- In general, avoid unnecessary use of special variables.
- PROGV is mainly for writing interpreters for languages embedded
- in Lisp. If you want to bind a list of values to a list of
- lexical variables, use
- (MULTIPLE-VALUE-BIND (..) (VALUES-LIST ..) ..)
- or
- (MULTIPLE-VALUE-SETQ (..) (VALUES-LIST ..))
- instead. Most decent compilers can optimize this expression.
- However, use of this idiom is not to be encouraged unless absolutely
- necessary.
-
- - CATCH and THROW. Often a named BLOCK and RETURN-FROM are
- more appropriate. Use UNWIND-PROTECT when necessary.
-
- - Destructive operations, such as NCONC, SORT, DELETE,
- RPLACA, and RPLACD, should be used carefully and sparingly.
- In general, trust the garbage collector: allocate new
- data structures when you need them.
-
- To improve the readability of your code,
-
- - Don't use any C{A,D}R functions with more than two
- letters between the C and the R. When nested, they become
- hard to read. If you have complex data structures, you
- are often better off describing them with a DEFSTRUCT,
- even if the type is LIST. If you must use C{A,D}R, try to
- use destructuring-bind instead, or at least SECOND, THIRD,
- NTH, NTHCDR, etc.
-
- - Use COND instead of IF and PROGN. In general, don't use PROGN if
- there is a way to write the code within an implicit
- PROGN. For example,
- (IF (FOO X)
- (PROGN (PRINT "hi there") 23)
- 34)
- should be written using COND instead.
-
- - Never use a 2-argument IF or a 3-argument IF with a second
- argument of NIL unless you want to emphasize the return value;
- use WHEN and UNLESS instead. You will want to emphasize the
- return value when the IF clause is embedded within a SETQ,
- such as (SETQ X (IF (EQ Y Z) 2 NIL)). If the second argument
- to IF is the same as the first, use OR instead: (OR P Q) rather
- than (IF P P Q). Use UNLESS instead of (WHEN (NOT ..) ..)
- but not instead of (WHEN (NULL ..) ..).
-
- - Use COND instead of nested IF statements. Be sure to check for
- unreachable cases, and eliminate those cond-clauses.
-
- - Use backquote, rather than explicit calls to LIST, CONS, and
- APPEND, whenever writing a form which produces a Lisp form, but
- not as a general substitute for LIST, CONS and APPEND. LIST,
- CONS and APPEND usually allocate new storage, but lists produced
- by backquote may involve destructive modification (e.g., ,.).
-
- - Make the names of special (global) variables begin and end
- with an asterisk (*): (DEFVAR *GLOBAL-VARIABLE*)
- Some programmers will mark the beginning and end of an internal
- global variable with a percent (%) or a period (.).
- Make the names of constants begin and end with a plus (+):
- (DEFCONSTANT +E+ 2.7182818)
- This helps distinguish them from lexical variables. Some people
- prefer to use macros to define constants, since this avoids
- the problem of accidentally trying to bind a symbol declared
- with defconstant.
-
- - If your program is built upon an underlying substrate which is
- implementation-dependent, consider naming those functions and
- macros in a way that visually identifies them, either by placing
- them in their own package, or prepending a character like a %, .,
- or ! to the function name. Note that many programmers use the
- $ as a macro character for slot access, so it should be avoided
- unless you're using it for that purpose.
-
- - Don't use property lists. Instead, use an explicit hash table.
- This helps avoid problems caused by the symbol being in the wrong
- package, accidental reuse of property keys from other
- programs, and allows you to customize the structure of the table.
-
- - Use the most specific construct that does the job. This lets
- readers of the code see what you intended when writing the code.
- For example, don't use SETF if SETQ will do (e.g., for lexical
- variables). Use the most specific predicate to test your conditions.
- If you intend for a function to be a predicate, have it return T
- for true, not just non-NIL.
-
- - When NIL is used as an empty list, use () in your code. When NIL
- is used as a boolean, use NIL. Similarly, use NULL to test for an
- empty list, NOT to test a logical value. Use ENDP to test for the
- end of a list, not NULL.
-
- - Don't use the &AUX lambda-list keyword. It is always clearer to
- define local variables using LET or LET*.
-
- - When using RETURN and RETURN-FROM to exit from a block, don't
- use (VALUES ..) when returning only one value, except if you
- are using it to suppress extra multiple values from the first
- argument.
-
- - If you want a function to return no values (i.e., equivalent to
- VOID in C), use (VALUES) to return zero values. This signals
- to the reader that the function is used mainly for side-effects.
-
- - (VALUES (VALUES 1 2 3)) returns only the first value, 1.
- You can use (VALUES (some-multiple-value-function ..)) to suppress
- the extra multiple values from the function. Use MULTIPLE-VALUE-PROG1
- instead of PROG1 when the multiple values are significant.
-
- - When using MULTIPLE-VALUE-BIND and DESTRUCTURING-BIND, don't rely
- on the fact that NIL is used when values are missing. This is
- an error in some implementations of DESTRUCTURING-BIND. Instead,
- make sure that your function always returns the proper number of
- values.
-
- Documentation:
-
- - Comment your code. Use three semicolons in the left margin before
- the definition for major explanations. Use two semicolons that
- float with the code to explain the routine that follows. Two
- semicolons may also be used to explain the following line when the
- comment is too long for the single semicolon treatment. Use
- a single semicolon to the right of the code to explain a particular
- line with a short comment. The number of semicolons used roughly
- corresponds with the length of the comment. Put at least one blank
- line before and after top-level expressions.
-
- - Include documentation strings in your code. This lets users
- get help while running your program without having to resort to
- the source code or printed documentation.
-
- Issues related to macros:
-
- - Never use a macro instead of a function for efficiency reasons.
- Declaim the function as inline -- for example,
- (DECLAIM (INLINE ..))
- This is *not* a magic bullet -- be forewarned that inline
- expansions can often increase the code size dramatically. INLINE
- should be used only for short functions where the tradeoff is
- likely to be worthwhile: inner loops, types that the compiler
- might do something smart with, and so on.
-
- - When defining a macro that provides an implicit PROGN, use the
- &BODY lambda-list keyword instead of &REST.
-
- - Use gensyms for bindings within a macro, unless the macro lets
- the user explicitly specify the variable. For example:
- (defmacro foo ((iter-var list) body-form &body body)
- (let ((result (gensym "RESULT")))
- `(let ((,result nil))
- (dolist (,iter-var ,list ,result)
- (setq ,result ,body-form)
- (when ,result
- ,@body)))))
- This avoids errors caused by collisions during macro expansion
- between variable names used in the macro definition and in the
- supplied body.
-
- - Use a DO- prefix in the name of a macro that does some kind of
- iteration, WITH- when the macro establishes bindings, and
- DEFINE- or DEF- when the macro creates some definitions. Don't
- use the prefix MAP- in macro names, only in function names.
-
- - Don't create a new iteration macro when an existing function
- or macro will do.
-
- - Don't define a macro where a function definition will work just
- as well -- remember, you can FUNCALL or MAPCAR a function but
- not a macro.
-
- - The LOOP and SERIES macros generate efficient code. If you're
- writing a new iteration macro, consider learning to use one
- of them instead.
-
- File Modularization:
-
- - If your program involves macros that are used in more than one
- file, it is generally a good idea to put such macros in a separate
- file that gets loaded before the other files. The same things applies
- to primitive functions. If a macro is complicated, the code that
- defines the macro should be put into a file by itself. In general, if
- a set of definitions form a cohesive and "independent" whole, they
- should be put in a file by themselves, and maybe even in their own
- package. It isn't unusual for a large Lisp program to have files named
- "site-dependent-code", "primitives.lisp", and "macros.lisp". If a file
- contains primarily macros, put "-macros" in the name of the file.
-
- Stylistic preferences:
-
- - Use (SETF (CAR ..) ..) and (SETF (CDR ..) ..) in preference to
- RPLACA and RPLACD. Likewise (SETF (GET ..) ..) instead of PUT.
-
- - Use INCF, DECF, PUSH and POP instead instead of the corresponding
- SETF forms.
-
- - Many programmers religiously avoid using CATCH, THROW, BLOCK,
- PROG, GO and TAGBODY. Tags and go-forms should only be necessary
- to create extremely unusual and complicated iteration constructs. In
- almost every circumstance, a ready-made iteration construct or
- recursive implementation is more appropriate.
-
- - Don't use LET* where LET will do. Don't use LABELS where FLET
- will do. Don't use DO* where DO will do.
-
- - Don't use DO where DOTIMES or DOLIST will do.
-
- - If you like using MAPCAR instead of DO/DOLIST, use MAPC when
- no result is needed -- it's more efficient, since it doesn't
- cons up a list. If a single cumulative value is required, use
- REDUCE. If you are seeking a particular element, use FIND,
- POSITION, or MEMBER.
-
- - If using REMOVE and DELETE to filter a sequence, don't use the
- :test-not keyword or the REMOVE-IF-NOT or DELETE-IF-NOT functions.
- Use COMPLEMENT to complement the predicate and the REMOVE-IF
- or DELETE-IF functions instead.
-
- - Use complex numbers to represent points in a plane.
-
- - Don't use lists where vectors are more appropriate. Accessing the
- nth element of a vector is faster than finding the nth element
- of a list, since the latter requires pointer chasing while the
- former requires simple addition. Vectors also take up less space
- than lists. Use adjustable vectors with fill-pointers to
- implement a stack, instead of a list -- using a list continually
- conses and then throws away the conses.
-
- - When adding an entry to an association list, use ACONS, not
- two calls to CONS. This makes it clear that you're using an alist.
-
- - If your association list has more than about 10 entries in it,
- consider using a hash table. Hash tables are often more efficient.
-
- - When you don't need the full power of CLOS, consider using
- structures instead. They are often faster, take up less space, and
- easier to use.
-
- - Use PRINT-UNREADABLE-OBJECT when writing a print-function.
-
- - Use WITH-OPEN-FILE instead of OPEN and CLOSE.
-
- - When a HANDLER-CASE clause is executed, the stack has already
- unwound, so dynamic bindings that existed when the error
- occured may no longer exist when the handler is run. Use
- HANDLER-BIND if you need this.
-
- - When using CASE and TYPECASE forms, if you intend for the form
- to return NIL when all cases fail, include an explicit OTHERWISE
- clause. If it would be an error to return NIL when all cases
- fail, use ECASE, CCASE, ETYPECASE or CTYPECASE instead.
-
- - Use local variables in preference to global variables whenever
- possible. Do not use global variables in lieu of parameter passing.
- Global variables can be used in the following circumstances:
- * When one function needs to affect the operation of
- another, but the second function isn't called by the first.
- (For example, *load-pathname* and *break-on-warnings*.)
- * When a called function needs to affect the current or future
- operation of the caller, but it doesn't make sense to accomplish
- this by returning multiple values.
- * To provide hooks into the mechanisms of the program.
- (For example, *evalhook*, *, /, and +.)
- * Parameters which, when their value is changed, represent a
- major change to the program.
- (For example, *print-level* and *print-readably*.)
- * For state that persists between invocations of the program.
- Also, for state which is used by more than one major program.
- (For example, *package*, *readtable*, *gensym-counter*.)
- * To provide convenient information to the user.
- (For example, *version* and *features*.)
- * To provide customizable defaults.
- (For example, *default-pathname-defaults*.)
- * When a value affects major portions of a program, and passing
- this value around would be extremely awkward. (The example
- here is output and input streams for a program. Even when
- the program passes the stream around as an argument, if you
- want to redirect all output from the program to a different
- stream, it is much easier to just rebind the global variable.)
-
- Correctness and efficiency issues:
-
- - In CLtL2, IN-PACKAGE does not evaluate its argument. Use defpackage
- to define a package and declare the external (exported)
- symbols from the package.
-
- - The ARRAY-TOTAL-SIZE-LIMIT may be as small as 1024, and the
- CALL-ARGUMENTS-LIMIT may be as small as 50.
-
- - Novices often mistakenly quote the conditions of a CASE form.
- For example, (case x ('a 3) ..) is incorrect. It would return
- 3 if x were the symbol QUOTE. Use (case x (a 3) ..) instead.
-
- - Avoid using APPLY to flatten lists. (apply #'append list-of-lists)
- is compiled into a function call, and can run into problems with
- the CALL-ARGUMENTS-LIMIT. Use REDUCE or MAPCAR instead:
- (reduce #'append list-of-lists :from-end t)
- (mapcan #'copy-list list-of-lists)
- The second will often be more efficient (see note below about choosing
- the right algorithm). Beware of calls like (apply f (mapcar ..)).
-
- - NTH must cdr down the list to reach the elements you are
- interested in. If you don't need the structural flexibility of
- lists, try using vectors and the ELT function instead.
-
- - Don't use quoted constants where you might later destructively
- modify them. For example, instead of writing '(c d) in
- (defun foo ()
- (let ((var '(c d)))
- ..))
- write (list 'c 'd) instead. Using a quote here can lead to
- unexpected results later. If you later destructively modify the
- value of var, this is self-modifying code! Some Lisp compilers
- will complain about this, since they like to make constants
- read-only. Modifying constants has undefined results in ANSI CL.
- See also the answer to question [3-13].
-
- Similarly, beware of shared list structure arising from the use
- of backquote. Any sublist in a backquoted expression that doesn't
- contain any commas can share with the original source structure.
-
- - Don't proclaim unsafe optimizations, such as
- (proclaim '(optimize (safety 0) (speed 3) (space 1)))
- since this yields a global effect. Instead, add the
- optimizations as local declarations to small pieces of
- well-tested, performance-critical code:
- (defun well-tested-function ()
- (declare (optimize (safety 0) (speed 3) (space 1)))
- ..)
- Such optimizations can remove run-time type-checking; type-checking
- is necessary unless you've very carefully checked your code
- and added all the appropriate type declarations.
-
- - Some programmers feel that you shouldn't add declarations to
- code until it is fully debugged, because incorrect
- declarations can be an annoying source of errors. They recommend
- using CHECK-TYPE liberally instead while you are developing the code.
- On the other hand, if you add declarations to tell the
- compiler what you think your code is doing, the compiler can
- then tell you when your assumptions are incorrect.
- Declarations also make it easier for another programmer to read
- your code.
-
- - Don't change the compiler optimization with an OPTIMIZE
- proclamation or declaration until the code is fully debugged
- and profiled. When first writing code you should say
- (declare (optimize (safety 3))) regardless of the speed setting.
-
- - Depending on the optimization level of the compiler, type
- declarations are interpreted either as (1) a guarantee from
- you that the variable is always bound to values of that type,
- or (2) a desire that the compiler check that the variable is
- always bound to values of that type. Use CHECK-TYPE if (2) is
- your intention.
-
- - If you get warnings about unused variables, add IGNORE
- declarations if appropriate or fix the problem. Letting such
- warnings stand is a sloppy coding practice.
-
- To produce efficient code,
-
- - choose the right algorithm. For example, consider seven possible
- implementations of COPY-LIST:
-
- (defun copy-list (list)
- (let ((result nil))
- (dolist (item list result)
- (setf result (append result (list item))))))
-
- (defun copy-list (list)
- (let ((result nil))
- (dolist (item list (nreverse result))
- (push item result))))
-
- (defun copy-list (list)
- (mapcar #'identity list))
-
- (defun copy-list (list)
- (let ((result (make-list (length list))))
- (do ((original list (cdr original))
- (new result (cdr new)))
- ((null original) result)
- (setf (car new) (car original)))))
-
- (defun copy-list (list)
- (when list
- (let* ((result (list (car list)))
- (tail-ptr result))
- (dolist (item (cdr list) result)
- (setf (cdr tail-ptr) (list item))
- (setf tail-ptr (cdr tail-ptr))))))
-
- (defun copy-list (list)
- (loop for item in list collect item))
-
- (defun copy-list (list)
- (if (consp list)
- (cons (car list)
- (copy-list (cdr list)))
- list))
-
- The first uses APPEND to tack the elements onto the end of the list.
- Since APPEND must traverse the entire partial list at each step, this
- yields a quadratic running time for the algorithm. The second
- implementation improves on this by iterating down the list twice; once
- to build up the list in reverse order, and the second time to reverse
- it. The efficiency of the third depends on the Lisp implementation,
- but it is usually similar to the second, as is the fourth. The fifth
- algorithm, however, iterates down the list only once. It avoids the
- extra work by keeping a pointer (reference) to the last cons of the
- list and RPLACDing onto the end of that. Use of the fifth algorithm
- may yield a speedup. Note that this contradicts the earlier dictum to
- avoid destructive functions. To make more efficient code one might
- selectively introduce destructive operations in critical sections of
- code. Nevertheless, the fifth implementation may be less efficient in
- Lisps with cdr-coding, since it is more expensive to RPLACD cdr-coded
- lists. Depending on the implementation of nreverse, however,
- the fifth and second implementations may be doing the same
- amount of work. The sixth example uses the Loop macro, which usually
- expands into code similar to the third. The seventh example copies
- dotted lists, and runs in linear time. It's equivalent to the other
- linear-time examples in a lisp that is properly tail-recursive.
-
- - use type declarations liberally in time-critical code, but
- only if you are a seasoned Lisp programmer. Appropriate type
- declarations help the compiler generate more specific and
- optimized code. It also lets the reader know what assumptions
- were made. For example, if you only use fixnum arithmetic,
- adding declarations can lead to a significant speedup. If you
- are a novice Lisp programmer, you should use type declarations
- sparingly, as there may be no checking to see if the
- declarations are correct. Wrong declarations can lead to errors
- in otherwise correct code, and can limit the reuse of code
- in other contexts. Depending on the Lisp compiler, it may also
- be necessary to declare the type of results using THE, since
- some compilers don't deduce the result type from the inputs.
-
- - check the code produced by the compiler by using the
- disassemble function
-
- ----------------------------------------------------------------
- [1-3] Where can I learn about implementing Lisp interpreters and compilers?
-
- Books about Lisp implementation include:
-
- 1. John Allen
- "Anatomy of Lisp"
- McGraw-Hill, 1978. 446 pages. ISBN 0-07-001115-X
-
- 2. Samuel Kamin
- "Programming Languages, An Interpreter-Based Approach"
- Addison-Wesley. ISBN 0-201-06824-9
- Includes sources to several interpreters for Lisp-like
- languages, and a pointer to sources via anonymous ftp.
-
- 3. Sharam Hekmatpour
- "Lisp: A Portable Implementation"
- Prentice Hall, 1985. ISBN 0-13-537490-X.
- Describes a portable implementation of a small dynamic
- Lisp interpreter (including C source code).
-
- 4. Peter Henderson
- "Functional Programming: Application and Implementation"
- Prentice-Hall (Englewood Cliffs, NJ), 1980. 355 pages.
-
- 5. Peter M. Kogge
- "The Architecture of Symbolic Computers"
- McGraw-Hill, 1991. ISBN 0-07-035596-7.
- Includes sections on memory management, the SECD and
- Warren Abstract Machines, and overviews of the various
- Lisp Machine architectures.
-
- 6. Daniel P. Friedman, Mitchell Wand, and Christopher T. Haynes
- "Essentials of Programming Languages"
- MIT Press, 1992, 536 pages. ISBN 0-262-06145-7.
- Teaches fundamental concepts of programming language
- design by using small interpreters as examples. Covers
- most of the features of Scheme. Includes a discussion
- of parameter passing techniques, object oriented languages,
- and techniques for transforming interpreters to allow
- their implementation in terms of any low-level language.
- Also discusses scanners, parsers, and the derivation of
- a compiler and virtual machine from an interpreter.
- Source files available by anonymous ftp from cs.indiana.edu
- in the directory /pub/eopl (129.79.254.191).
-
- 7. Also see the proceedings of the biannual ACM Lisp and
- Functional Programming conferences, and the implementation
- notes for CMU Common Lisp.
- ----------------------------------------------------------------
- [1-4] What does CLOS, PCL, X3J13, CAR, CDR, ... mean?
-
- Glossary of acronyms:
- CAR Originally meant "Contents of Address portion of Register",
- which is what CAR actually did on the IBM 704.
- CDR Originally meant "Contents of Decrement portion of
- Register", which is what CDR actually did
- on the IBM 704. Pronounced "Cudder".
- LISP Originally from "LISt Processing"
- GUI Graphical User Interface
- CLOS Common Lisp Object System. The object oriented
- programming standard for Common Lisp. Based on
- Symbolics FLAVORS and Xerox LOOPS, among others.
- Pronounced either as "See-Loss" or "Closs". See also PCL.
- PCL Portable Common Loops. A portable CLOS implementation.
- Available by anonymous ftp from parcftp.xerox.com:pcl/.
- LOOPS Lisp Object Oriented Programming System. A predecessor
- to CLOS on Xerox Lisp machines.
- X3J13 Subcommittee of the ANSI committee X3 which is
- working on the ANSI Standardization of Common Lisp.
- ANSI American National Standards Institute
- CL Common Lisp
- SC22/WG16 The full name is ISO/IEC JTC 1/SC 22/WG 16. It stands
- for International Organization for
- Standardization/International Electronics(?)
- Congress(?) Joint Technical Committee 1, Subcommittee 22,
- Working Group 16. This long-winded name is the ISO
- working group working on an international Lisp standard,
- (i.e., the ISO analogue to X3J13).
- CLtL1 First edition of Guy Steele's book,
- "Common Lisp the Language".
- CLtL2 Second edition of Guy Steele's book,
- "Common Lisp the Language".
-
- SICP Abelson and Sussman's book "Structure and
- Interpretation of Computer Programs".
- SCOOPS An experimental object-oriented programming
- language for Scheme.
- R3RS Revised^3 Report on the Algorithmic Language Scheme.
- R4RS Revised^4 Report on the Algorithmic Language Scheme.
- ----------------------------------------------------------------
- [1-5] Where can I get a copy of the draft ANSI standard for Common Lisp?
-
- The draft proposed American National Standard for Common Lisp is under
- public review until November 23, 1992.
-
- Hard copies of the draft may be purchased from Global Engineering
- Documents, Inc., 2805 McGaw Avenue, Irvine, CA 92714, 1-800-854-7179,
- 714-261-1455 for a single copy price of $80 ($104 international).
- Copies of the TeX sources and Unix-compressed DVI files may be
- obtained by anonymous FTP from parcftp.xerox.com in the directory
- /pub/cl/document/*. The file Reviewer-Notes.text should be read before
- ftp'ing the other files.
-
- There is no mechanism for submitting Public Review comments by e-mail.
- Comments on the draft must be submitted in hard copy format BOTH to X3
- Secretariat, Attn: Lynn Barra, 1250 Eye Street NW, Suite 200,
- Washington, DC 20005-3922 AND to American National Standards Institute,
- Attn: BSR Center, 11 West 42nd St. 13th Floor, New York, NY 10036.
-
- ----------------------------------------------------------------
- [1-6] Lisp Job Postings
-
- The LISP-JOBS mailing list exists to help programmers find Lisp
- programming positions, and to help companies with Lisp programming
- positions find capable Lisp programmers. (Lisp here means Lisp-like
- languages, including Scheme.)
-
- Material appropriate for the list includes Lisp job announcements and
- resumes from Lisp programmers (which should be sent only once) should
- be sent to lisp-jobs@amc.com. Administrative requests (e.g., to be
- added to the list) should be sent to lisp-jobs-request@amc.com.
-